package com.bobo.web.provider.util.commutils;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class HttpClientUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
    private static final int HTTP_TIMEOUT = 4000;//从服务器读取数据的timeout
    private static final int HTTP_CON_TIMEOUT = 4000;//和服务器建立连接的timeout
    private static final int HTTP_CON_REQ_TIMEOUT = 500;//从连接池获取连接的timeout
    private static final int HTTP_MAX_PERROUTE = 500;//路由的默认最大连接
    private static final int HTTP_MAX_TOTAL = 1000;//整个连接池连接的最大值
    private static final int KEEP_ALIVE = 2000;
    private static final String DEF_CHARSET = "UTF-8";

    private static CloseableHttpClient httpClient = null;
    private static RequestConfig defaultRequestConfig = null;

    static {
        ConnectionKeepAliveStrategy connectionKeepAliveStrategy = new ConnectionKeepAliveStrategy() {
            @Override
            public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) {
                return KEEP_ALIVE;
            }
        };
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        SocketConfig socketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
        connManager.setDefaultSocketConfig(socketConfig);
        connManager.setMaxTotal(HTTP_MAX_TOTAL);
        connManager.setDefaultMaxPerRoute(HTTP_MAX_PERROUTE);
        defaultRequestConfig = RequestConfig.custom().setSocketTimeout(HTTP_TIMEOUT).setConnectTimeout(HTTP_CON_TIMEOUT)
                .setConnectionRequestTimeout(HTTP_CON_REQ_TIMEOUT).build();
        httpClient = HttpClients.custom().setKeepAliveStrategy(connectionKeepAliveStrategy).setConnectionManager(connManager)
                .setDefaultRequestConfig(defaultRequestConfig).build();
    }

    public static String get(String baseUrl, Map<String, Object> params, int timeout, String encoding)
            throws Exception {
        String url = genUrl(params, baseUrl, encoding);
        return get(url, timeout);
    }

    public static String get(String url, Map<String, Object> params) throws Exception {
        // 这个方法有坑，不会转义 url
        return get(url, params, null);
    }

    public static String get(String url, Map<String, Object> params, Map<String, String> headers) throws Exception {
        // 这个方法有坑，不会转义 url，不要直接用
        logger.info("get start url = {}, params = {}, headers = {} ", url, params, headers);
        String response = null;
        long start = System.currentTimeMillis();
        try {
            String geturl = url;
            if (params != null && params.size() != 0) {
                geturl = genUrl(params, url, DEF_CHARSET);
            }
            logger.info("getURL ={}", geturl);
            HttpGet httpGet = new HttpGet(URLDecoder.decode(geturl, DEF_CHARSET));
            logger.info("encode url = {}", URLDecoder.decode(geturl, DEF_CHARSET));
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).build();
            httpGet.setConfig(requestConfig);
            if (null != headers) {
                for (String headerName : headers.keySet()) {
                    httpGet.setHeader(headerName, headers.get(headerName));
                }
            }
            response = httpClient.execute(httpGet, new JsonResponseHandler());
        } catch (Exception e) {
            logger.error("get url = {}, params = {}, headers = {}, error = {} ", url, params, headers, e);
            throw e;
        } finally {
            long latency = System.currentTimeMillis() - start;
            logger.info(">>>>>HttpClient monitor,url:" + url + ",latency(ms):" + latency);
        }
        logger.info("get end url = {}, params = {}, headers = {} ", url, params, headers);
        return response;
    }

    public static String getWithHeader(String url, Map<String, Object> params, Map<String, String> headers, int timeout) throws Exception {
        logger.info("get start url = {}, params = {}, headers = {} ", url, params, headers);
        String response;
        long start = System.currentTimeMillis();
        try {
            String geturl = url;
            if (!CollectionUtils.isEmpty(params)) {
                geturl = genUrl(params, url, DEF_CHARSET);
            }
            logger.info("getURL ={}", geturl);
            HttpGet httpGet = new HttpGet(geturl);
            logger.info("encode url = {}", URLEncoder.encode(geturl, DEF_CHARSET));
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(timeout).build();
            httpGet.setConfig(requestConfig);
            if (null != headers) {
                for (String headerName : headers.keySet()) {
                    httpGet.setHeader(headerName, headers.get(headerName));
                }
            }
            response = httpClient.execute(httpGet, new JsonResponseHandler());
            logger.info("get url with headers,response = {}", response);
        } catch (Exception e) {
            logger.error("get url = {}, params = {}, headers = {}, error = {} ", url, params, headers, e);
            throw e;
        } finally {
            long latency = System.currentTimeMillis() - start;
            logger.info(">>>>>HttpClient monitor,url:" + url + ",latency(ms):" + latency);
        }
        logger.info("get end url = {}, params = {}, headers = {} ", url, params, headers);
        return response;
    }


    public static String get(String url, int timeout) throws Exception {
        try {
            long st = System.currentTimeMillis();
            if (timeout < 0) {
                return null;
            }
            HttpGet httpget = new HttpGet(url);
            logger.info("Send Request url={}", url);
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(timeout).build();
            httpget.setConfig(requestConfig);
            logger.info("Send http request,url={}", url);
            String response = httpClient.execute(httpget, new JsonResponseHandler());
            if (logger.isInfoEnabled()) {
                logger.info(url + " time used:" + (System.currentTimeMillis() - st));
            }
            return response;
        } catch (Exception e) {
            logger.error("http error.url:" + url, e);
            throw e;
        }
    }

    public static int delete(String url, int timeout) throws Exception {
        try {
            long st = System.currentTimeMillis();
            if (timeout < 0) {
                return 0;
            }
            HttpDelete httpDelete = new HttpDelete(url);
            logger.info("Send Request url={}", url);
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(timeout).build();
            httpDelete.setConfig(requestConfig);
            CloseableHttpResponse response = httpClient.execute(httpDelete);
            if (logger.isInfoEnabled()) {
                logger.info(url + " time used:" + (System.currentTimeMillis() - st));
            }
            return response.getStatusLine().getStatusCode();
        } catch (Exception e) {
            logger.error("http error.url:" + url, e);
            throw e;
        }
    }

    public static String get(String url, String body, int timeout) throws IOException {
        HttpGetWithEntity httpGet = new HttpGetWithEntity();

        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("ids", body));

        RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).setSocketTimeout(timeout).build();
        httpGet.setConfig(requestConfig);
        try {
            httpGet.setEntity(new UrlEncodedFormEntity(nvps));
        } catch (UnsupportedEncodingException e) {
            logger.error("set get entity error! url:{} body:{}", url, body);
            throw e;
        }

        return httpClient.execute(httpGet, new JsonResponseHandler());
    }


    public static String encode(String str, String encode) {
        if (encode == null) {
            return str;
        }
        try {
            return URLEncoder.encode(str, encode);
        } catch (UnsupportedEncodingException e) {
            return str;
        }
    }

    public static String genUrl(Map<String, Object> params, String baseUrl) {
        return genUrl(params, baseUrl, null);
    }

    public static String genUrl(Map<String, Object> params, String baseUrl, final String encoding) {
        StringBuilder url = new StringBuilder(baseUrl);
        url.append("?");
        url.append(
                Joiner.on("&").join(Maps.transformEntries(params, new Maps.EntryTransformer<String, Object, String>() {
                    @Override
                    public String transformEntry(String key, Object value) {
                        if (key == null) {
                            return "";
                        }
                        if (value == null) {
                            return key + "=";
                        }
                        return key + "=" + encode(value.toString(), encoding);
                    }

                }).values()));
        return url.toString();
    }

    public static String postJSON(String url, Object object) throws IOException {
        return postJSONWithHeader(url, object, new HashMap<String, String>());
    }

    public static String postJSONWithHeader(String url, Object object, Map<String, String> header) throws IOException {
        JSONObject json = JsonUtils.toJSONObject(object);
        logger.info("Post data,url = {},params = {}", url, json);
        CloseableHttpResponse response = null;
        HttpPost httpPost = null;
        try {
            httpPost = new HttpPost(url);
            StringEntity jsonEntity = new StringEntity(json.toJSONString(), DEF_CHARSET);
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).build();
            httpPost.setConfig(requestConfig);
            httpPost.setHeader("Content-Type", "application/json");
            for (Map.Entry<String, String> e : header.entrySet()) {
                httpPost.setHeader(e.getKey(), e.getValue());
            }
            httpPost.setEntity(jsonEntity);

            response = httpClient.execute(httpPost);
            logger.info("Post data,response status line = {}", response.getStatusLine());
            HttpEntity entity = response.getEntity();
            String rt = EntityUtils.toString(entity);
            EntityUtils.consume(entity);
            return rt;
        } catch (Exception e) {
            logger.error("Post data error,url = {},params = {}", url, json, e);
            throw e;
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    logger.error("关闭输出流时出错，url = {}", url, e);
                }
            }
            httpPost.releaseConnection();
        }
    }

    public static String post(String url, Map<String, Object> params, Map<String, String> headers) throws Exception {
        logger.info("post start url = {}, params = {}, headers = {} ", url, params, headers);
        String result = null;
        long start = System.currentTimeMillis();
        CloseableHttpResponse response = null;
        HttpPost httpPost = null;
        try {
            httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).build();
            httpPost.setConfig(requestConfig);
            if (headers != null && headers.size() > 0) {
                for (String headerName : headers.keySet()) {
                    httpPost.setHeader(headerName, headers.get(headerName));
                }
            }
            List<NameValuePair> paramsList = Lists.newArrayList();
            if (params != null && params.size() > 0) {
                for (String paramName : params.keySet()) {
                    if (params.get(paramName) != null) {
                        paramsList.add(new BasicNameValuePair(paramName, params.get(paramName).toString()));
                    }
                }
            }
            httpPost.setEntity(new UrlEncodedFormEntity(paramsList, DEF_CHARSET));
            result = httpClient.execute(httpPost, new JsonResponseHandler());
        } catch (Exception e) {
            logger.error("post start url = {}, params = {}, headers = {}, error = {} ", url, params, headers, e);
            throw e;
        } finally {
            long latency = System.currentTimeMillis() - start;
            logger.info(">>>>>HttpClient monitor,url:" + url + ",latency(ms):" + latency);
            httpPost.releaseConnection();
        }
        return result;
    }


    public static class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {

        @Override
        public String getMethod() {
            return "GET";
        }
    }

    public static String post(String url, Map<String, Object> params) throws Exception {
        logger.info("post start url = {}, params = {}", url, params);
        String result = null;
        long start = System.currentTimeMillis();
        try {
            HttpPost httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig).build();
            httpPost.setConfig(requestConfig);
            List<NameValuePair> paramsList = Lists.newArrayList();
            if (params != null && params.size() > 0) {
                for (String paramName : params.keySet()) {
                    if (params.get(paramName) != null) {
                        paramsList.add(new BasicNameValuePair(paramName, params.get(paramName).toString()));
                    }
                }
            }
            httpPost.setEntity(new UrlEncodedFormEntity(paramsList, "UTF-8"));
            result = httpClient.execute(httpPost, new JsonResponseHandler());
        } catch (Exception e) {
            logger.error("post start url = {}, params = {}, error = {} ", url, params, e);
            throw e;
        } finally {
            long latency = System.currentTimeMillis() - start;
            logger.info(">>>>>HttpClient monitor,url:" + url + ",latency(ms):" + latency);
        }
        return result;
    }

    /**
     * 发送XML格式的post数据
     * @param urlStr
     * @param dataMap
     * @return
     */
    public static String creatPostAndTransData(String urlStr,Map<String, String> dataMap) {
        String line = "";
        StringBuffer resultSting = new StringBuffer();
        try {
            //声明URL
            URL url = new URL(urlStr);
            //1.创建链接
            URLConnection con = url.openConnection();
            //2.封装报文传输进行传输
            //调用getXmlInfo(Map<String, Object> dataMap)进行报文的封装
            String xmlInfo = getXmlInfo(dataMap);
            byte[] xmlData = xmlInfo.getBytes();
            con.setDoOutput(true);
            con.setDoInput(true);
            con.setUseCaches(false);
            con.setRequestProperty("Cache-Control", "no-cache");
            con.setRequestProperty("Content-Type", "text/xml");
            con.setRequestProperty("Content-length",String.valueOf(xmlData.length));
            OutputStreamWriter out = new OutputStreamWriter(
                    con.getOutputStream());
            out.write(new String(xmlInfo.getBytes("ISO-8859-1")));
            out.flush();
            out.close();
            //3.获取返回报文
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    con.getInputStream()));
            //对返回值报文进行打印
            for (line = br.readLine(); line != null; line = br.readLine()) {
                System.out.println(">>>>>>>>>>>>>>>>>>>返回的结果报文内容为:---------"+line);
                //对返回的报文进行拼接,然后返回给业务层,在业务层进行判断
                resultSting.append(line);
            }
            return resultSting.toString();
        } catch (Exception e) {
            logger.error("报文转化异常",e);
        }
        return resultSting.toString();
    }
    private static String getXmlInfo(Map<String, String> dataMap) {
        String mch_id = dataMap.get("mch_id").toString();
        String nonce_str = dataMap.get("nonce_str").toString();
        String sign = dataMap.get("sign").toString();
        StringBuilder sb = new StringBuilder();
        sb.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
        sb.append("<ECSRoot>");
        sb.append("    <Body>");
        sb.append("        <mch_id>" + mch_id + "</mch_id>");
        sb.append("        <nonce_str>" + nonce_str + "</nonce_str>");
        sb.append("        <sign>" + sign + "</sign>");
        sb.append("    </Body>");
        sb.append("</ECSRoot>");
        return sb.toString();
    }
}
